<template>
    <!-- OSS上传组件 -->
    <div class="oss-upload-container">
        <div class="policy_one_title">生成STS临时访问凭证 + 前端手动上传</div>
        <el-upload class="upload-demo" :http-request="customUpload" :limit="fileLimit" :on-exceed="handleExceed"
            :on-success="handleSuccess" :on-error="handleError" :file-list="fileList" :show-file-list="showFileList"
            :multiple="multiple" :accept="accept" :auto-upload="false" ref="uploadRef">
            <el-button type="primary">选择文件</el-button>
            <!-- <template #tip>
                <div class="el-upload__tip">
                    {{ tipText }}
                </div>
            </template> -->
        </el-upload>
        <el-button type="success" @click="submitUpload">
            执行上传
        </el-button>
        <div class="upload_img" v-if="reshowFlag">
            <img :src="reShowPictureUrl" alt="上传的图片回显" srcset="">
        </div>
    </div>
</template>

<script setup>
import { ref, defineProps, defineEmits, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import axios from 'axios';
import { nanoid } from 'nanoid'

onMounted(() => {
    // 页面加载，获取请求参数暂存到内存中
    getOssSignature();
})

// 父组件调用时传入的参数
const props = defineProps({
    // 限制文件数量
    fileLimit: {
        type: Number,
        default: 1
    },
    // 显示文件列表
    showFileList: {
        type: Boolean,
        default: true
    },
    // 允许多文件上传
    multiple: {
        type: Boolean,
        default: false
    },
    // 接受上传文件的类型，为空表示所有文件类型都可以
    accept: {
        type: String,
        default: ''
    },
    // 文件容量超出时的文本提示
    tipText: {
        type: String,
        default: '请上传文件，大小不超过50MB'
    }
});

// 定义手动上传组件
let uploadRef = ref()

// 定义父组件中的触发函数（触发父组件中的回调函数）
const emit = defineEmits(['upload-success', 'upload-error']);
// 上传组件上传的文件列表
const fileList = ref([]);

// 暂存上传的签名参数（这并不是一个好的设计，因为每次页面加载都会获取一次，应该存储到本地持久化，请求前先判断当前存储的参数是否已过期再决定是否重新获取）
const signatureParam = ref({});

// 回显的图片地址
let reShowPictureUrl = ref();
let reshowFlag = ref(false);

// 生成的全局唯一的文件名
let uniqueName = ref("");

// 手动上传执行函数
const submitUpload = () => {
    uploadRef.value.submit() // 当该函数触发，会执行customUpload函数
}

// 获取OSS上传参数（获STS临时凭证参数）
async function getOssSignature() {
    const { data: res } = await axios.get('http://localhost:9090/oss_sts/get_sts_signature');
    signatureParam.value = res;
    console.log("页面加载，重新获取签名参数===>", signatureParam.value);
}

// 超出文件数量限制时的处理
const handleExceed = (files) => {
    ElMessage.warning(`最多只能上传 ${props.fileLimit} 个文件`);
};

// 上传成功回调
const handleSuccess = (file) => {
    console.log("选择文件成功===>>>", file);
};

function showImg(file) {
    // 构造图片回显
    reShowPictureUrl.value = `${signatureParam.value.host}${signatureParam.value.dir}/${uniqueName.value}`;
    reshowFlag.value = true;
    ElMessage.success('上传成功');

    // 通知父组件执行下一步处理
    emit('upload-success', {
        name: file.name,
        url: reShowPictureUrl.value
    });

}

// 上传失败回调
const handleError = (err) => {
    ElMessage.error('上传失败，请稍后重试');
    emit('upload-error', err);
};

// 自定义上传处理函数
// 该函数触发的时机：当点击上传按钮并选择文件之后触发（自动：因为上传组件的auto-upload默认为true）
// 如果想要设置为手动触发，也就是手动上传，需要将auto-upload设置为false，然后设置一个上传按钮，给上传组件添加ref，手动调用ref.submit();就会触发该函数
const customUpload = async (options) => {
    const { file, onSuccess, onError } = options;

    console.log("上传的文件", file);

    try {
        // 构造请求参数对象
        const formData = new FormData();

        // 使用nanoid库构造全局唯一的图片名称
        // 获取文件后缀
        const fileExt = file.name.substring(file.name.lastIndexOf('.'))
        // 生成唯一文件名
        const uniqueFileName = `${nanoid()}${fileExt}`
        uniqueName.value = uniqueFileName;


        // 组装表单数据
        formData.append("success_action_status", "200");
        formData.append("policy", signatureParam.value.policy);
        formData.append("x-oss-signature", signatureParam.value.signature);
        formData.append("x-oss-signature-version", "OSS4-HMAC-SHA256");
        formData.append("x-oss-credential", signatureParam.value.x_oss_credential);
        formData.append("x-oss-date", signatureParam.value.x_oss_date);
        formData.append("key", signatureParam.value.dir + "/" + uniqueName.value); // 文件名
        formData.append("x-oss-security-token", signatureParam.value.security_token);
        formData.append("file", file); // file 必须为最后一个表单域

        // 发送上传请求到OSS
        const uploadRes = await axios.post(signatureParam.value.host, formData);

        if (uploadRes.status === 200) {
            onSuccess(file);
            showImg(file);
        } else {
            throw new Error('上传到OSS失败');
        }
    } catch (error) {
        console.error('上传出错:', error);
        onError(error);
    }
};
</script>

<style scoped>
.oss-upload-container {
    margin: 20px 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

.el-upload__tip {
    font-size: 12px;
    color: #606266;
    margin-top: 7px;
}

.policy_one_title {
    font-size: 20px;
    font-weight: 600;
    padding-bottom: 20px;
}

.upload_img img {
    width: 150px;
}
</style>